#include "State.h"
#include "fieldgenerator.h"
#include <cmath>
#include <tr1/random>
using namespace std;


PotentialField::PotentialField(const State& state):m_state(state) {
    //
}

void PotentialField::initField() {
    field = new vector< vector<double> >(m_state.rows, vector<double>(m_state.cols,0));
}

void PotentialField::clearCaches() {
    potentialCache.clear();
    quadCache.clear();
}

void PotentialField::visited(const Location& loc) {
    std::tr1::mt19937 eng;
    std::tr1::uniform_real<float> dist(0.5,0.6);
    (*field)[loc.row][loc.col]+=dist(eng);
}

double PotentialField::getFriendlyAntPotential(const Location& sourceLoc,const Location& targetLoc) {
    double dist=m_state.manhattan_distance(sourceLoc,targetLoc);
    return FRIENDLYANT_BASEPOTENTIAL*(1.0/dist);
}

double PotentialField::getEnemyAntPotential(const Location& sourceLoc,const Location& targetLoc) {
    double dist=m_state.manhattan_distance(sourceLoc,targetLoc);
    return ENEMYANT_BASEPOTENTIAL*(1.0/dist);
}

double PotentialField::getFoodPotential(const Location& sourceLoc,const Location& targetLoc) {
    double dist=m_state.manhattan_distance(sourceLoc,targetLoc);
    return FOOD_BASEPOTENTIAL*(1.0/dist);
}

double PotentialField::getPotential(const Location& loc) {
    if (potentialCache.find(loc)!=potentialCache.end()) {
	return potentialCache[loc];
    }
    double potential = 0;
    for(std::vector<Location>::const_iterator it = m_state.ants.begin();it!=m_state.ants.end(); it++) {
	if (loc!=*it) {
	    potential+=getFriendlyAntPotential(loc,*it);
	    potential+=getQuadrantPotential(*it);
	}
    }
    for(std::vector<Location>::const_iterator it = m_state.enemies.begin();it!=m_state.enemies.end(); it++) {
	potential+=getEnemyAntPotential(loc,*it);
	potential+=getQuadrantPotential(*it);
    }
    for(std::vector<Location>::const_iterator it = m_state.food.begin();it!=m_state.food.end(); it++) {
	potential+=getFoodPotential(loc,*it);
	potential+=getQuadrantPotential(*it);
    }
    potential -= ((*field)[loc.row][loc.col]);
    potentialCache[loc] = potential;
    return potential;
}

double PotentialField::getQuadrantPotential(const Location& loc) {
    if (quadCache.find(loc)!=quadCache.end()) {
	return quadCache[loc];
    }
    Location QuadStart,QuadEnd;
    const int  QUAD_SIZE=4;

    QuadStart.row = loc.row - loc.row%QUAD_SIZE;
    QuadStart.col = loc.col - loc.col%QUAD_SIZE;
    QuadEnd.row = (QuadStart.row+QUAD_SIZE> m_state.rows) ? m_state.rows : QuadStart.row+QUAD_SIZE;
    QuadEnd.col = (QuadStart.col+QUAD_SIZE > m_state.cols) ? m_state.cols : QuadStart.col+QUAD_SIZE;

    double quad_potential = 0;
    for(int i=QuadStart.row; i<QuadEnd.row; i++) {
	for (int j=QuadStart.col; j < QuadEnd.col ; j++) {
	    if (abs(m_state.grid[i][j].lastSeen-m_state.turn)> 10 ) {
		quad_potential+=20;
	    }
	    if (m_state.grid[i][j].ant ==-1) {
		quad_potential+=1;
	    }
	    if (m_state.grid[i][j].ant==0 ) {
		quad_potential+=5;
	    }
	    if (m_state.grid[i][j].ant>0) {
		quad_potential+=30;
	    }
	    if (m_state.grid[i][j].isFood) {
		quad_potential+=100;
	    }
	    if (m_state.grid[i][j].isWater) {
		quad_potential-=10;
	    }
	    quad_potential += m_state.grid[i][j].deadAnts.size()*30;
	}
    }
    quadCache[loc] = quad_potential;
    return quad_potential;
}

//Return the highest potential square adjacent to this square
bool PotentialField::higher(const Location& loc,int& direction) {
    Location higherLoc = loc;
    double max_potential = getPotential(loc);
    double loc_potential = max_potential;
    bool found = false;
    for(int d=0;d<TDIRECTIONS;d++) {
	Location nLoc = m_state.getLocation(loc,d);
	//skip unseen locations and water squares as comparison points
	if (m_state.grid[nLoc.row][nLoc.col].isWater ||
	    m_state.grid[nLoc.row][nLoc.col].lastSeen==0)
	    continue;

	double tempPotential  = getPotential(nLoc);
	Logger::theLogger() << loc.row << "," << loc.col << ",potential:"  << loc_potential << "=>" << nLoc.row << "," << nLoc.col << ",potential : "<< tempPotential << endl;
	if (tempPotential>max_potential) {
	    higherLoc = nLoc;
	    direction = d;
	    found = true;
	}
    }
    return found;
}

//Return the lowest potential square adjacent to this square
bool PotentialField::lower(const Location& loc,int& direction) {
    Location lowerLoc = loc;
    double max_potential = getPotential(loc);
    bool found = false;
    for(int d=0;d<TDIRECTIONS;d++) {
	Location nLoc = m_state.getLocation(loc,d);
	//skip unseen locations and water squares as comparison points
	if (m_state.grid[nLoc.row][nLoc.col].isWater ||
	    m_state.grid[nLoc.row][nLoc.col].lastSeen==0)
	    continue;
	double tempPotential  = getPotential(nLoc);
	if (tempPotential<max_potential) {
	    lowerLoc = nLoc;
	    direction = d;
	    found=true;
	}
    }
    return found;
}
